package org.jeecg.modules.bpm.controller;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.DelegationState;
import org.activiti.engine.task.Task;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.bpm.dto.BatchSuspendInfo;
import org.jeecg.modules.bpm.dto.ProcessInstHisDTO;
import org.jeecg.modules.bpm.dto.SuspendInfo;
import org.jeecg.modules.bpm.service.ActivitiService;
import org.jeecg.modules.bpm.service.impl.SuperTaskService;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import org.jeecg.modules.extbpm.process.entity.ExtActFlowData;
import org.jeecg.modules.extbpm.process.service.IExtActFlowDataService;
import org.jeecg.modules.extbpm.util.CommonRandomUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import lombok.extern.slf4j.Slf4j;

/**
 * 流程实例管理功能
 * 
 * @author scott
 */
@Slf4j
@RestController("actProcessInstanceController")
@RequestMapping("/act/processInstance")
public class ActProcessInstanceController {
	private final String START_USER_ID = CommonRandomUtil.trim("startUserId");
	private final String PROCESS_INSTANCE_ID = CommonRandomUtil.trim("processInstanceId");
	
	@Autowired
	protected RepositoryService repositoryService;
	@Autowired
	private HistoryService historyService;
	@Autowired
	private RuntimeService runtimeService;
	@Autowired
	private TaskService taskService;
	@Autowired
	private ActivitiService activitiService;
	@Autowired
	private SuperTaskService superTaskService;
	@Autowired
	private IExtActFlowDataService extActFlowDataService;
	@Autowired
	private ISysBaseAPI sysBaseAPI;

	/**
	 * 运行中流程实例数据
	 * 
	 * @param request
	 * @param response
	 * @return
	 */

	@GetMapping(value = "/list")
	public Result<IPage<ProcessInstHisDTO>> listRunning(HttpServletRequest request) {
		Result<IPage<ProcessInstHisDTO>> result = new Result<IPage<ProcessInstHisDTO>>();
		IPage<ProcessInstHisDTO> pageList = new Page<ProcessInstHisDTO>();
		
		HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
		if (CommonRandomUtil.isNotEmpty(request.getParameter(START_USER_ID))) {
			historicProcessInstanceQuery = historicProcessInstanceQuery.startedBy(request.getParameter(START_USER_ID));
		}
		if (CommonRandomUtil.isNotEmpty(request.getParameter(PROCESS_INSTANCE_ID))) {
			historicProcessInstanceQuery = historicProcessInstanceQuery.processInstanceId(request.getParameter(PROCESS_INSTANCE_ID));
		}

		String startTimeBegin = request.getParameter("startTime_begin");// 时间开始
		String startTimeEnd = request.getParameter("startTime_end");// 时间结束
		if (CommonRandomUtil.isNotEmpty(startTimeBegin)) {
			try {
				historicProcessInstanceQuery.startedAfter(DateUtils.parseDate(startTimeBegin, "yyyy-MM-dd"));
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		if (CommonRandomUtil.isNotEmpty(startTimeEnd)) {
			try {
				historicProcessInstanceQuery.startedBefore(DateUtils.parseDate(startTimeEnd, "yyyy-MM-dd"));
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}

		String endtimeBegin = request.getParameter("endTime_begin");// 时间开始
		String endtimeEnd = request.getParameter("endTime_end");// 时间结束
		if (CommonRandomUtil.isNotEmpty(endtimeBegin)) {
			try {
				historicProcessInstanceQuery.finishedAfter(DateUtils.parseDate(endtimeBegin, "yyyy-MM-dd"));
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		if (CommonRandomUtil.isNotEmpty(endtimeEnd)) {
			try {
				historicProcessInstanceQuery.finishedBefore(DateUtils.parseDate(endtimeEnd, "yyyy-MM-dd"));
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		//改造treegrid
		// 分页参数
		Integer pageNo = oConvertUtils.getInt(request.getParameter("pageNo"),1);
		Integer pageSize = oConvertUtils.getInt(request.getParameter("pageSize"),10);
		Integer start = (pageNo - 1) * pageSize;
		//Integer end = page * rows - 1;
		List<HistoricProcessInstance> list = historicProcessInstanceQuery.unfinished().orderByProcessInstanceStartTime().desc().listPage(start, pageSize);
		long count = historicProcessInstanceQuery.unfinished().count();
		List<ProcessInstHisDTO> records = new ArrayList<ProcessInstHisDTO>();
		ProcessInstHisDTO processInstHisDTO = null;
		for (HistoricProcessInstance hi : list) {
			String starttime = DateFormatUtils.format(hi.getStartTime(), "yyyy-MM-dd HH:mm:ss");
			String endtime = hi.getEndTime() == null ? "" : DateFormatUtils.format(hi.getEndTime(), "yyyy-MM-dd HH:mm:ss");

			long totalTimes = hi.getEndTime() == null ? (Calendar.getInstance().getTimeInMillis() - hi.getStartTime().getTime()) : (hi.getEndTime().getTime() - hi.getStartTime().getTime());

			long dayCount = totalTimes / (1000 * 60 * 60 * 24);// 计算天
			long restTimes = totalTimes % (1000 * 60 * 60 * 24);// 剩下的时间用于计于小时
			long hourCount = restTimes / (1000 * 60 * 60);// 小时
			restTimes = restTimes % (1000 * 60 * 60);
			long minuteCount = restTimes / (1000 * 60);

			String spendTimes = dayCount + "天" + hourCount + "小时" + minuteCount + "分";
			ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(hi.getProcessDefinitionId()).singleResult();
			log.debug("spendTimes：" + spendTimes);

			String isSuspended = "finished";
			String activityName = "";// 当前任务名称
			String activityUser = "";// 当前任务签收人
			String taskId = "";// 任务ID
			if (hi.getEndTime() == null) {
				// endtime为空表示流程实例未结束
				// 增加业务标题
				String bpmBizTitle = this.activitiService.getHisVarinst(WorkFlowGlobals.BPM_BIZ_TITLE, hi.getId());
				bpmBizTitle = oConvertUtils.isEmpty(bpmBizTitle)?processDefinition.getName():bpmBizTitle;
				ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(hi.getId()).singleResult();
				List<Task> tasks = taskService.createTaskQuery().processInstanceId(hi.getId()).list();
				if (tasks.size() > 1) {
					// 针对会签或者一个流程多个分支任务（一个流程实例，同时存在多个任务情况），流程实例报错处理--------------------
					isSuspended = "" + pi.isSuspended();
					activityName = CommonRandomUtil.trimToEmpty(tasks.get(0).getName());
					activityUser = CommonRandomUtil.trimToEmpty(tasks.get(0).getAssignee());
					taskId = oConvertUtils.getString(tasks.get(0).getId());
					processInstHisDTO = new ProcessInstHisDTO();
					processInstHisDTO.setId(hi.getId());
					processInstHisDTO.setBpmBizTitle(bpmBizTitle);
					processInstHisDTO.setParentId("");
					processInstHisDTO.setTaskId(taskId);
					processInstHisDTO.setName(activityName);
					processInstHisDTO.setAssignee("");
					processInstHisDTO.setPrcocessDefinitionName(processDefinition.getName());
					processInstHisDTO.setStartUserId(hi.getStartUserId());
					if(oConvertUtils.isNotEmpty(processInstHisDTO.getStartUserId())) {
		    			LoginUser sysUser = sysBaseAPI.getUserByName(processInstHisDTO.getStartUserId());
		    			processInstHisDTO.setStartUserName(sysUser.getRealname());
		    		}
					processInstHisDTO.setStartTime(starttime);
					processInstHisDTO.setEndTime(endtime);
					processInstHisDTO.setSpendTimes(spendTimes);
					processInstHisDTO.setIsSuspended(isSuspended);
					processInstHisDTO.setProcessDefinitionId(hi.getProcessDefinitionId());
					processInstHisDTO.setProcessInstanceId(hi.getId());
					int i = 1;
					List<ProcessInstHisDTO> childrens = new ArrayList<ProcessInstHisDTO>();
					ProcessInstHisDTO children = null;
					for (Task task : tasks) {
						isSuspended = "" + pi.isSuspended();
						activityName = CommonRandomUtil.trimToEmpty(task.getName());
						activityUser = CommonRandomUtil.trimToEmpty(task.getAssignee());
						taskId = oConvertUtils.getString(task.getId());
						children = new ProcessInstHisDTO();
						children.setId(hi.getId() + ":" + (i++));
						children.setBpmBizTitle(bpmBizTitle);
						children.setParentId(hi.getId());
						children.setTaskId(taskId);
						children.setName(activityName);
						children.setAssignee(activityUser);
						if(oConvertUtils.isNotEmpty(children.getAssignee())) {
			    			LoginUser sysUser = sysBaseAPI.getUserByName(children.getAssignee());
			    			children.setAssigneeName(sysUser!=null?sysUser.getRealname():children.getAssignee());
			    		}
						children.setPrcocessDefinitionName(processDefinition.getName());
						children.setStartUserId(hi.getStartUserId());
						if(oConvertUtils.isNotEmpty(children.getStartUserId())) {
			    			LoginUser sysUser = sysBaseAPI.getUserByName(children.getStartUserId());
			    			children.setStartUserName(sysUser.getRealname());
			    		}
						children.setStartTime(starttime);
						children.setEndTime(endtime);
						children.setSpendTimes(spendTimes);
						children.setIsSuspended(isSuspended);
						children.setProcessDefinitionId(hi.getProcessDefinitionId());
						children.setProcessInstanceId(hi.getId());
						childrens.add(children);
					}
					processInstHisDTO.setChildren(childrens);
					records.add(processInstHisDTO);
				} else {
					for (Task task : tasks) {
						isSuspended = "" + pi.isSuspended();
						activityName = CommonRandomUtil.trimToEmpty(task.getName());
						activityUser = CommonRandomUtil.trimToEmpty(task.getAssignee());
						taskId = oConvertUtils.getString(task.getId());
						processInstHisDTO = new ProcessInstHisDTO();
						processInstHisDTO.setId(hi.getId());
						processInstHisDTO.setBpmBizTitle(bpmBizTitle);
						processInstHisDTO.setParentId("");
						processInstHisDTO.setTaskId(taskId);
						processInstHisDTO.setName(activityName);
						processInstHisDTO.setAssignee(activityUser);
						if(oConvertUtils.isNotEmpty(processInstHisDTO.getAssignee())) {
			    			LoginUser sysUser = sysBaseAPI.getUserByName(processInstHisDTO.getAssignee());
			    			processInstHisDTO.setAssigneeName(sysUser!=null?sysUser.getRealname():processInstHisDTO.getAssignee());
			    		}
						processInstHisDTO.setPrcocessDefinitionName(processDefinition.getName());
						processInstHisDTO.setStartUserId(hi.getStartUserId());
						if(oConvertUtils.isNotEmpty(processInstHisDTO.getStartUserId())) {
			    			LoginUser sysUser = sysBaseAPI.getUserByName(processInstHisDTO.getStartUserId());
			    			processInstHisDTO.setStartUserName(sysUser!=null?sysUser.getRealname():processInstHisDTO.getStartUserId());
			    		}
						processInstHisDTO.setStartTime(starttime);
						processInstHisDTO.setEndTime(endtime);
						processInstHisDTO.setSpendTimes(spendTimes);
						processInstHisDTO.setIsSuspended(isSuspended);
						processInstHisDTO.setProcessDefinitionId(hi.getProcessDefinitionId());
						processInstHisDTO.setProcessInstanceId(hi.getId());
						records.add(processInstHisDTO);
					}
				}
			}
		}

		pageList.setRecords(records);
		
		log.debug(" count  : " + count);
		pageList.setTotal(count);
		result.setSuccess(true);
		result.setResult(pageList);
		return result;
	}
	
	
	/**
     * 进行委派操作
     * @param processInstanceId 流程部署ID
     */
	@GetMapping(value = "reassign")
	public Result reassign(@RequestParam("taskId") String taskId, @RequestParam("userName") String assignUserId,HttpServletRequest request) {
		Task  task = activitiService.getTask(taskId);
		//去掉最后面的逗号
		if(oConvertUtils.isNotEmpty(assignUserId) && assignUserId.endsWith(",")) {
			assignUserId = assignUserId.substring(0, assignUserId.length() -1);
		}
		LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
		taskService.setOwner(task.getId(), sysUser.getUsername());
		//TODO 委派给多个人好使吗？
		taskService.setAssignee(task.getId(), assignUserId);
		return Result.ok("委派成功!");
	}
	
	
	
	/**
     * 暂停正在运行的流程实例
     * @param processInstanceId 流程实例ID
     */
	@GetMapping(value = "suspend")
	public Result suspend(@RequestParam("processInstanceId") String processInstanceId, HttpServletRequest request) {
		activitiService.suspend(processInstanceId);
		return Result.ok("挂起成功!");
	}
	
	/**
     * 启动暂停的流程实例
     * @param processInstanceId 流程实例ID
     */
	@GetMapping(value = "restart")
	public Result restart(@RequestParam("processInstanceId") String processInstanceId, HttpServletRequest request) {
		activitiService.restart(processInstanceId);
		return Result.ok("启动成功!");
	}
	
	/**
     * 	暂停正在运行的流程实例(批量处理)
     */
	@PostMapping(value = "batchSuspend")
	public Result<BatchSuspendInfo> batchSuspend(@RequestBody BatchSuspendInfo batchSuspendInfo, HttpServletRequest request) {
		Result<BatchSuspendInfo> result = new Result<BatchSuspendInfo>();
		List<SuspendInfo> data = batchSuspendInfo.getData();
		if(data==null||data.size()<=0) {
			result.error500("请选择数据");
			return result;
		}
		int sucessCount = 0;
		for(SuspendInfo info :data) {
			try {
				LambdaQueryWrapper<ExtActFlowData> queryWrapper = new LambdaQueryWrapper<ExtActFlowData>();
				queryWrapper.eq(ExtActFlowData::getFormDataId, info.getDataId());
				queryWrapper.eq(ExtActFlowData::getRelationCode, info.getFlowCode());
				ExtActFlowData extActFlowData = extActFlowDataService.getOne(queryWrapper);
				if(extActFlowData!=null) {
					info.setProcInstId(extActFlowData.getProcessInstId());
					activitiService.suspend(extActFlowData.getProcessInstId());
					info.setStatus("1");//处理成功
					sucessCount++;
					continue;
				}
			} catch (Exception e) {
				log.error(e.getMessage(),e);
			}
			info.setStatus("0");//处理失败
		}
		batchSuspendInfo.setData(data);
		batchSuspendInfo.setSucessCount(sucessCount);
		result.setResult(batchSuspendInfo);
		result.success("处理完成！");
		return result;
	}
	
	/**
     * 	启动暂停的流程实例(批量处理)
     */
	@PostMapping(value = "batchRestart")
	public Result<BatchSuspendInfo> batchRestart(@RequestBody BatchSuspendInfo batchSuspendInfo, HttpServletRequest request) {
		Result<BatchSuspendInfo> result = new Result<BatchSuspendInfo>();
		List<SuspendInfo> data = batchSuspendInfo.getData();
		if(data==null||data.size()<=0) {
			result.error500("请选择数据");
			return result;
		}
		int sucessCount = 0;
		for(SuspendInfo info :data) {
			try {
				LambdaQueryWrapper<ExtActFlowData> queryWrapper = new LambdaQueryWrapper<ExtActFlowData>();
				queryWrapper.eq(ExtActFlowData::getFormDataId, info.getDataId());
				queryWrapper.eq(ExtActFlowData::getRelationCode, info.getFlowCode());
				ExtActFlowData extActFlowData = extActFlowDataService.getOne(queryWrapper);
				if(extActFlowData!=null) {
					info.setProcInstId(extActFlowData.getProcessInstId());
					activitiService.restart(extActFlowData.getProcessInstId());
					info.setStatus("1");//处理成功
					sucessCount++;
					continue;
				}
			} catch (Exception e) {
				log.error(e.getMessage(),e);
			}
			info.setStatus("0");//处理失败
		}
		batchSuspendInfo.setData(data);
		batchSuspendInfo.setSucessCount(sucessCount);
		result.setResult(batchSuspendInfo);
		result.success("处理完成！");
		return result;
	}
	
	/**
     * 作废运行中的流程实例
     * @param processInstanceId 流程实例ID
     */
	@GetMapping(value = "close")
	public Result close(@RequestParam("processInstanceId") String processInstanceId, HttpServletRequest request) {
		runtimeService.deleteProcessInstance(processInstanceId, "主动作废流程");
		return Result.ok("作废成功!");
	}
	
	
	/**
	 * 跳转到某个节点
	 * @param taskId  当前节点
	 * @param skipNode 跳转到那个节点
	 * @param request 
	 * @return
	 * @throws Exception 
	 */
	@GetMapping(value = "skipNode")
	public Result skipNode(@RequestParam("taskId") String taskId, @RequestParam("skipTaskNode") String skipNode,HttpServletRequest request) throws Exception {
		//TODO 流程变量参数
		//Map<String, Object> map = var.getVariableMap(processHandle.getTpProcesspros());
		//判断是否是委托任务，被委托人先解决委托
		Task task=taskService.createTaskQuery().taskId(taskId).active().singleResult();
		if (StringUtils.isNotBlank(task.getOwner())) {
			DelegationState delegationState = task.getDelegationState();
            switch (delegationState) {
            case PENDING:
                taskService.resolveTask(taskId);
                break;
            case RESOLVED:
                //委托任务已经完成
                break;

            default:
                //不是委托任务
                break;
            }
		}
		if("end".equals(skipNode)){
			superTaskService.endProcess(taskId);
		}else{
			superTaskService.goProcessTaskNode(taskId,skipNode);
		}
		return Result.ok("跳转成功!");
	}
	
	/**
	 * 通过当前taskId获取所有的流程任务节点
	 * @param taskId  当前节点
	 * @param request 
	 * @return
	 * @throws Exception 
	 */
	@GetMapping(value = "getAllTask")
	public Result getAllTask(@RequestParam("taskId") String taskId,HttpServletRequest request) throws Exception {
		List<Map> ls = activitiService.getAllTaskNode(taskId);
		List<Map> list = new ArrayList<Map>();
		for(Map map :ls) {
			String taskKey = (String)map.get("taskKey");
			if(taskKey==null||taskKey.startsWith("exclusiveGateway")
					||taskKey.startsWith("parallelGateway")
					||taskKey.startsWith("callSubProcess")
					||taskKey.startsWith("end")||taskKey.startsWith("start1")) {
				continue;
			}
			list.add(map);
		}
		return Result.ok(list);
	}

}
